package be.rhea.player;
import java.io.InputStream;

import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.AudioDevice;
import javazoom.jl.player.Player;

public class MP3Player {

    private final static int NOTSTARTED = 0;
    private final static int PLAYING = 1;
    private final static int PAUSED = 2;
    private final static int FINISHED = 3;

    // the player actually doing all the work
    private final Player player;

    // locking object used to communicate with player thread
    private final Object playerLock = new Object();

    // status variable what player thread is doing/supposed to do
    private int playerStatus = NOTSTARTED;
    
    private MP3PlayerListener listener;

    public MP3Player(final InputStream inputStream) throws JavaLayerException {
        this.player = new Player(inputStream,  javazoom.jl.player.FactoryRegistry.systemRegistry().createAudioDevice()) {
        	public boolean play(int frames) throws JavaLayerException
        	{
        		boolean ret = true;

        		// report to listener
        		while (frames-- > 0 && ret)
        		{
        			ret = decodeFrame();
        		}

        		if (!ret)
        		{
        				synchronized (this)
        				{
        					close();
        				}

        				// report to listener
        				if(listener != null) listener.playbackFinished();
        		}
        		return ret;
        	}
        	
        };
    }

    public MP3Player(final InputStream inputStream, final AudioDevice audioDevice) throws JavaLayerException {
        this.player = new Player(inputStream, audioDevice);
    }
    
    public void addMP3PlayerListener(MP3PlayerListener listener) {
    	this.listener = listener;
    }

    /**
     * Starts playback (resumes if paused)
     */
    public void play() throws JavaLayerException {
        synchronized (playerLock) {
            switch (playerStatus) {
                case NOTSTARTED:
                    final Runnable r = new Runnable() {
                        public void run() {
                    		if(listener != null) listener.playbackStarted();
                        	
                            playInternal();
                        }
                    };
                    final Thread t = new Thread(r);
                    t.setDaemon(true);
                    t.setPriority(Thread.MAX_PRIORITY);
                    playerStatus = PLAYING;
                    t.start();
                    break;
                case PAUSED:
                    resume();
                    break;
                default:
                    break;
            }
        }
    }

    /**
     * Pauses playback. Returns true if new state is PAUSED.
     */
    public boolean pause() {
        synchronized (playerLock) {
            if (playerStatus == PLAYING) {
                playerStatus = PAUSED;
            }
            return playerStatus == PAUSED;
        }
    }

    /**
     * Resumes playback. Returns true if the new state is PLAYING.
     */
    public boolean resume() {
        synchronized (playerLock) {
            if (playerStatus == PAUSED) {
                playerStatus = PLAYING;
                playerLock.notifyAll();
            }
            return playerStatus == PLAYING;
        }
    }

    /**
     * Stops playback. If not playing, does nothing
     */
    public void stop() {
        synchronized (playerLock) {
            playerStatus = FINISHED;
            playerLock.notifyAll();
        }
    }

    private void playInternal() {
        while (playerStatus != FINISHED) {
            try {
                if (!player.play(1)) {
                    break;
                }
            } catch (final JavaLayerException e) {
                break;
            }
            // check if paused or terminated
            synchronized (playerLock) {
                while (playerStatus == PAUSED) {
                    try {
                        playerLock.wait();
                    } catch (final InterruptedException e) {
                        // terminate player
                        break;
                    }
                }
            }
        }
        close();
    }

    /**
     * Closes the player, regardless of current state.
     */
    public void close() {
        synchronized (playerLock) {
            playerStatus = FINISHED;
        }
        try {
            player.close();
        } catch (final Exception e) {
            // ignore, we are terminating anyway
        }
    }

//    // demo how to use
//    public static void main(String[] argv) {
//        try {
//            FileInputStream input = new FileInputStream("C:\\programming\\workspace\\ProjectorControllerServer\\media\\assholle_calling.mp3"); 
//            MP3Player player = new MP3Player(input);
//            
//            MP3PlayerListener listener =  new MP3PlayerListener() {
//				
//				@Override
//				public void playbackStarted() {
//					System.out.println("Started");
//					
//				}
//				
//				@Override
//				public void playbackFinished() {
//					System.out.println("Finished");
//					
//				}
//			};
//			player.addMP3PlayerListener(listener);
//
//            // start playing
//            player.play();
//
//            // after 5 secs, pause
//            Thread.sleep(5000);
//         //   player.pause();     
//
//            // after 5 secs, resume
//            Thread.sleep(5000);
//       //     player.resume();
//            Thread.sleep(5000);
//        } catch (final Exception e) {
//            throw new RuntimeException(e);
//        }
//    }

}